/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ola_speke.h"

#include "hichain.h"
#include "ola_base_utils.h"
#include "ola_def.h"
#include "ola_error.h"
#include "ola_log.h"
#include "ola_negotiation.h"
#include "ola_pal_os.h"
#include "ola_security.h"
#include "ola_random.h"
#include "ola_sdk_api.h"
#include "securec.h"

#define SPEKE_NEGO_TIMEOUT      (15 * 1000)

#define OLA_SPEKE_HANDLE        "OLA"
#define SPEKE_SESSIONKEY_LEN    16
#define SPEKE_SESSION_ID_LEN    32
#define SPEKE_MAX_BUFF_SIZE     1024

#define SESSION_ID_LEN          32
#define MAX_COAP_PKT_LEN        2048
#define INFORM_PEER_MESSAGE     0x8080

typedef struct {
    hc_handle hcHandle;
    unsigned char dataEncKey[SPEKE_SESSIONKEY_LEN];
    const void *businessData;
} BindSession;

typedef void *hc_handle;
typedef int (*SpekeFinishCb)(int errorCode, const BindSession *session);

/* speke 协商角色 */
typedef enum {
    SPEKE_UNKNOWN = 0,
    SPEKE_CLIENT,
    SPEKE_SERVER
} SpekeType;

typedef enum {
    E2E_PAKE_TYPE,
    E2E_STS_TYPE,
} E2eOperateType;

typedef struct {
    BindSession *bindSession;
    void *data;
    char sessionId[SESSION_ID_LEN + 1];
} E2eHiChainContext;

typedef enum {
    OLA_SPEKE_STEP_INVALID = -1,
    OLA_SPEKE_STEP_FIRST,
    OLA_SPEKE_STEP_FINISH
} OlaSpekeStep;

typedef struct {
    char sessionId[SPEKE_SESSION_ID_LEN + 1];
    unsigned long long lastMsgTime;
} OlaSpekeInfo;

static OlaNegotiationCb g_negoCb = {NULL, NULL};
static OlaSpekeInfo g_spekeInfo = {0};
static OlaNegoStatus g_negoStatus = OLA_NEGO_STATUS_UNINIT;

/* speke协商过程中的错误码 */
static int g_spekeErrCode = 0;

/* 用于构建hichain实例的基础信息 */
struct session_identity g_sessionIdentity = {0};

/* hichain实例的上下文 */
E2eHiChainContext g_e2eContext = {0};

/* hichain实例上下文中秘钥协商上下文 */
static BindSession *g_spekeSession = NULL;

/* 代表speke会话协商完成，可以基于该会话发送业务数据了 */
static int SpekeFinishedNotify(int errorCode, const BindSession *spekeSession, const void *businessData)
{
    OlaLogInfo("speke errcode [%d]", errorCode);

    if (spekeSession == NULL) {
        OlaLogInfo("speke session is null");
        return OLA_ERROR;
    }

    if (businessData == NULL) {
        OlaLogError("speke businessData is null");
        return OLA_ERROR;
    }

    SpekeFinishCb spekeFinishProc = (SpekeFinishCb)businessData;
    return spekeFinishProc(errorCode, spekeSession);
}

/* 协商session过程中获取PIN */
static int SpekeGetPinCode(unsigned char *buffer, unsigned int bufferLen, unsigned int *realLen)
{
    if ((buffer == NULL) || (bufferLen == 0) || (realLen == NULL)) {
        OlaLogError("invalid param");
        return OLA_ERROR;
    }
    if (g_negoCb.getPinCodeCb == NULL) {
        OlaLogError("pin code callback null");
        return OLA_ERROR;
    }
    unsigned int len = bufferLen;
    if (g_negoCb.getPinCodeCb((unsigned char *)buffer, (unsigned int *)&len) != OLA_OK) {
        OlaLogError("get pinCode failed");
        return OLA_ERROR;
    }
    if (len > bufferLen) {
        OlaLogError("pinCode failed");
        return OLA_ERROR;
    }
    *realLen = len;
    OlaLogInfo("get pincode success");
    return OLA_OK;
}

static void ClearAndFreeJsonObj(cJSON* json)
{
    OlaClearJsonString(json);
    if (json != NULL) {
        cJSON_Delete(json);
    }
    return;
}

/* 初始化speke协商会话 */
static BindSession *SpekeInitSession(SpekeType spekeType, const void *businessData, bool useDefaultPinCode)
{
    (void)useDefaultPinCode;
    (void)(spekeType);
    BindSession *session = (BindSession *)OlaMalloc(sizeof(BindSession));
    if (session == NULL) {
        OlaLogError("malloc session failed\r\n");
        return NULL;
    }

    (void)memset_s(session, sizeof(BindSession), 0, sizeof(BindSession));
    session->businessData = businessData;
    return session;
}

/* 解析speke数据的sessionId和securityData字段 */
static int GetPakeJsonMsgInfo(const cJSON *json, char **sessionStr, cJSON **securityData)
{
    *sessionStr = OlaGetJsonString(json, OLA_STRING_SESSIONID);
    if ((*sessionStr == NULL) || (strlen(*sessionStr) == 0)) {
        OlaLogError("parse session json failed\r\n");
        return OLA_CJSON_ERR_PARSE;
    }

    *securityData = cJSON_GetObjectItem(json, OLA_STRING_SECURITYDT);
    if (*securityData == NULL) {
        OlaLogError("parse securityData json failed\r\n");
        return OLA_CJSON_ERR_PARSE;
    }

    return OLA_OK;
}

static int SetPakeContext(const hc_handle hcHandle, BindSession *bindSession, const char *sessionStr,
    const OlaBuffer *responseBuff)
{
    if (bindSession == NULL) {
        OlaLogError("invalid param");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }

    bindSession->hcHandle = hcHandle;

    /* 保存sessionId, 用于 Transmit 函数中构建回复消息 */
    (void)memset_s(&g_e2eContext, sizeof(E2eHiChainContext), 0, sizeof(E2eHiChainContext));
    if (strncpy_s(g_e2eContext.sessionId, SESSION_ID_LEN + 1, sessionStr, strlen(sessionStr)) != 0) {
        OlaLogError("copy sessionId failed");
        return OLA_SECUREC_ERR_STRNCPY;
    }

    g_e2eContext.bindSession = bindSession;
    g_e2eContext.data = (void *)responseBuff;

    set_context(hcHandle, &g_e2eContext);

    return OLA_OK;
}

static int32_t ConfirmReceiveRequest(const struct session_identity *identity, int32_t operationCode)
{
    OlaLogInfo("func in");
    (void)identity;
    (void)operationCode;

    /* 当前不关注处理的阶段，直接返回OK即可 */
    return HC_OK;
}

/* 协商获取PIN码的钩子函数 */
static void GetProtocolParams(const struct session_identity *identity, int32_t operationCode,
    struct hc_pin *pin, struct operation_parameter *para)
{
    if ((identity == NULL) || (pin == NULL) || (para == NULL)) {
        OlaLogError("invalid param");
        return;
    }

    OlaLogInfo("operationCode[%d]", operationCode);
    if ((operationCode == BIND) || (operationCode == AUTH_KEY_AGREEMENT)) {
        int ret = SpekeGetPinCode(pin->pin, HC_PIN_BUFF_LEN, (unsigned int *)&(pin->length));
        if (ret != 0) {
            OlaLogError("get pinCode failed, ret[%d]\r\n", ret);
            return;
        }
        para->key_length = SPEKE_SESSIONKEY_LEN;
    } else {
        OlaLogError("operation code is error %d\r\n", operationCode);
        return;
    }

    return;
}

static void SetServiceResult(const struct session_identity *identity, int32_t result)
{
    if ((result != END_FAILED) && (result != END_SUCCESS)) {
        OlaLogInfo("service result[%d]", result);
        return;
    }

    if (identity == NULL) {
        OlaLogError("identity is null\r\n");
        return;
    }

    E2eHiChainContext *e2eContext = (E2eHiChainContext *)identity->context;
    if (e2eContext == NULL) {
        OlaLogError("e2eContext is null\r\n");
        return;
    }

    uint32_t sessionId = identity->session_id;

    OlaLogInfo("speke result[%d]", result);

    if (result == END_FAILED) {
        OlaLogInfo("speke end failed");
    } else if (result == END_SUCCESS) {
        if ((sessionId == E2E_PAKE_TYPE) && (e2eContext->bindSession != NULL)) {
            int ret = SpekeFinishedNotify(OLA_OK, e2eContext->bindSession,
                ((BindSession *)(e2eContext->bindSession))->businessData);
            if (ret != OLA_OK) {
                OlaLogError("Notify bind finished failed, ret[%d]\r\n", ret);
            }
        } else {
            OlaLogError("session_id[%d] or bindSession invalid", sessionId);
        }
    }

    (void)memset_s(e2eContext, sizeof(E2eHiChainContext), 0, sizeof(E2eHiChainContext));

    return;
}

static void SetSessionKey(const struct session_identity *identity, const struct hc_session_key *sessionKey)
{
    if ((identity == NULL) || (sessionKey == NULL)) {
        OlaLogError("invalid param");
        return;
    }

    if (identity->session_id != E2E_PAKE_TYPE) {
        OlaLogError("session id error %u\r\n", identity->session_id);
        return;
    }

    E2eHiChainContext *e2eContext = (E2eHiChainContext *)identity->context;
    if (e2eContext == NULL) {
        OlaLogError("e2eContext null");
        return;
    }

    BindSession *bindSession = (BindSession *)(e2eContext->bindSession);
    if (bindSession == NULL) {
        OlaLogError("bindSession null");
        return;
    }

    if (sessionKey->length < SPEKE_SESSIONKEY_LEN) {
        OlaLogError("length[%u] is error\r\n", sessionKey->length);
        return;
    }

    if (memcpy_s(bindSession->dataEncKey, sizeof(bindSession->dataEncKey),
        sessionKey->session_key, SPEKE_SESSIONKEY_LEN) != 0) {
        OlaLogError("memcpy sessionKey error\r\n");
    }
    return;
}

/* 构建错误回复报文 */
static void BuildErrorPacket(const char *sessionId, OlaBuffer *e2eBuf, int errorCode)
{
    if ((sessionId == NULL) || (e2eBuf == NULL) || (e2eBuf->buf == NULL)) {
        return;
    }

    int ret = snprintf_s((char *)e2eBuf->buf + e2eBuf->len, e2eBuf->size - e2eBuf->len, e2eBuf->size - e2eBuf->len - 1,
        "{\"%s\": \"%s\", \"%s\": {\"%s\": %d, \"%s\": {\"%s\": %d}}}", OLA_STRING_SESSIONID, sessionId,
        OLA_STRING_SECURITYDT, OLA_STRING_MESSAGE, INFORM_PEER_MESSAGE,
        OLA_STRING_PAYLOAD, OLA_STRING_ERROR_CODE, errorCode);
    if (ret < 0) {
        OlaLogError("snprintf_s error %d", e2eBuf->len);
        return;
    }
    e2eBuf->len = e2eBuf->len + ret;

    return;
}

/* 构建speke消息回复包, PAKE: password authenticated key exchange */
static void BuildPakePacket(const E2eHiChainContext *context, const char *data)
{
    if ((context == NULL) || (data == NULL)) {
        OlaLogError("invalid param");
        return;
    }
    OlaBuffer *e2eBuf = (OlaBuffer *)context->data;
    if ((e2eBuf == NULL) || (e2eBuf->buf == NULL)) {
        OlaLogError("e2eBuf invalid");
        return;
    }

    int ret = snprintf_s((char *)e2eBuf->buf + e2eBuf->len, e2eBuf->size - e2eBuf->len, e2eBuf->size - e2eBuf->len - 1,
        "{\"%s\":\"%s\", \"%s\":%s}", OLA_STRING_SESSIONID, context->sessionId,
        OLA_STRING_SECURITYDT, data);
    if (ret < 0) {
        OlaLogError("snprintf_s error, len[%u], size[%u]", e2eBuf->len, e2eBuf->size);
        return;
    }
    e2eBuf->len = e2eBuf->len + ret;

    return;
}

static int BuildResponsePacket(unsigned int type, const E2eHiChainContext *e2eContext,
    const void *data, unsigned int length)
{
    if ((length == 0) || (length > MAX_COAP_PKT_LEN) || (data == NULL) || (e2eContext == NULL)) {
        OlaLogError("length[%u] is error\r\n", length);
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }

    char *tmpData = OlaMalloc(length + 1);
    if (tmpData == NULL) {
        OlaLogError("malloc tmpData failed\r\n");
        return OLA_OS_ERR_MALLOC;
    }

    (void)memset_s(tmpData, length + 1, 0, length + 1);

    int ret;
    do {
        if (memcpy_s(tmpData, length + 1, data, length) != 0) {
            OlaLogError("memcpy data error\r\n");
            ret = OLA_SECUREC_ERR_MEMCOPY;
            break;
        }

        if (type != E2E_PAKE_TYPE) {
            OlaLogError("type is error, %u\r\n", type);
            ret = OLA_ERROR;
            break;
        }
        BuildPakePacket(e2eContext, tmpData);
        ret = OLA_OK;
    } while (0);

    OlaFree(tmpData);
    return ret;
}

static void Transmit(const struct session_identity *identity, const void *data, uint32_t length)
{
    if ((identity == NULL) || (data == NULL)) {
        OlaLogError("invalid param\r\n");
        return;
    }

    E2eHiChainContext *e2eContext = (E2eHiChainContext *)identity->context;
    if (e2eContext == NULL) {
        OlaLogError("e2eContext is null\r\n");
        return;
    }

    int ret = BuildResponsePacket(identity->session_id, e2eContext, data, length);
    if (ret != OLA_OK) {
        OlaLogError("build response packet ret[%d]", ret);
        OlaBuffer *e2eBuf = (OlaBuffer *)e2eContext->data;
        if ((e2eBuf == NULL) || (e2eBuf->buf == NULL)) {
            return;
        }
        BuildErrorPacket(e2eContext->sessionId, e2eBuf, ret);
    }

    return;
}

static hc_handle StartInstance(unsigned int sessionId)
{
    const char *text = OLA_SPEKE_HANDLE;

    (void)memset_s(&g_sessionIdentity, sizeof(g_sessionIdentity), 0, sizeof(g_sessionIdentity));
    g_sessionIdentity.session_id = sessionId;
    if (memcpy_s(g_sessionIdentity.package_name.name, HC_PACKAGE_NAME_BUFF_LEN, text, strlen(text)) != 0) {
        OlaLogError("copy package_name failed\r\n");
        return NULL;
    }
    g_sessionIdentity.package_name.length = strlen(text);

    if (memcpy_s(g_sessionIdentity.service_type.type, HC_PACKAGE_NAME_BUFF_LEN, text, strlen(text)) != 0) {
        OlaLogError("copy service_type failed\r\n");
        return NULL;
    }
    g_sessionIdentity.service_type.length = strlen(text);

    struct hc_call_back callBack;
    (void)memset_s(&callBack, sizeof(callBack), 0, sizeof(callBack));
    callBack.confirm_receive_request = ConfirmReceiveRequest;
    callBack.get_protocol_params = GetProtocolParams;
    callBack.set_service_result = SetServiceResult;
    callBack.set_session_key = SetSessionKey;
    callBack.transmit = Transmit;

    hc_handle handle = get_instance(&g_sessionIdentity, HC_ACCESSORY, &callBack);
    if (handle == NULL) {
        OlaLogError("get instance failed\r\n");
        return NULL;
    }

    return handle;
}

static int ReceivePakeData(BindSession *bindSession, const char *sessionId,
    const cJSON *securityData, const OlaBuffer *responseBuff)
{
    hc_handle hcHandle = bindSession != NULL ? bindSession->hcHandle : NULL;
    if (hcHandle == NULL) {
        /* 当前handle无效，则需要重新创建实例 */
        hcHandle = StartInstance((unsigned int)E2E_PAKE_TYPE);
        if (hcHandle == NULL) {
            OlaLogError("start pake instance failed\r\n");
            return OLA_ERROR;
        }
    }

    int ret = SetPakeContext(hcHandle, bindSession, sessionId, responseBuff);
    if (ret != OLA_OK) {
        OlaLogError("set pake context failed ret %d\r\n", ret);
        return OLA_ERROR;
    }

    ret = receive_data_with_json_object(hcHandle, (void *)securityData);
    if (ret != OLA_OK) {
        OlaLogError("process pake data failed ret %d\r\n", ret);
        return OLA_ERROR;
    }

    return OLA_OK;
}

static int SpekeProcessPacket(BindSession *spekeSession, const char *requestPayload, OlaBuffer *responseBuff)
{
    if ((spekeSession == NULL) || (requestPayload == NULL)) {
        OlaLogError("process bind packet failed for null input\r\n");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }

    if ((responseBuff == NULL) || (responseBuff->buf == NULL)) {
        OlaLogError("responsebuff is null\r\n");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }

    /* 解析报文 */
    cJSON *json = cJSON_Parse(requestPayload);
    if (json == NULL) {
        OlaLogError("get requestPayload json failed\r\n");
        return OLA_CJSON_ERR_PARSE;
    }

    char *sessionId = NULL;
    cJSON *securityData = NULL;

    int ret = GetPakeJsonMsgInfo(json, &sessionId, &securityData);
    if (ret != OLA_OK) {
        OlaLogError("get pake json info failed, ret[%d]\r\n", ret);
        ClearAndFreeJsonObj(json);
        return OLA_ERROR;
    }

    ret = ReceivePakeData(spekeSession, sessionId, securityData, responseBuff);
    if (ret != OLA_OK) {
        OlaLogError("process pake error\r\n");
        ClearAndFreeJsonObj(json);
        /* 统一回复错误通知报文 */
        BuildErrorPacket(sessionId, responseBuff, ret);
        return ret;
    }

    ClearAndFreeJsonObj(json);
    return OLA_OK;
}

/* 释放协商会话 */
static void SpekeFreeSession(BindSession *spekeSession)
{
    if (spekeSession == NULL) {
        OlaLogError("free session failed for null input\r\n");
        return;
    }

    if (spekeSession->hcHandle != NULL) {
        destroy(&(spekeSession->hcHandle));
        spekeSession->hcHandle = NULL;
    }

    (void)memset_s(spekeSession, sizeof(BindSession), 0, sizeof(BindSession));
    OlaFree(spekeSession);
    return;
}

static void SpekeSetNegoStatus(OlaNegoStatus status)
{
    OlaLogInfo("set speke nego status[%d]", status);
    g_negoStatus = status;
}

static OlaNegoStatus SpekeGetNegoStatus(void)
{
    return g_negoStatus;
}

static BindSession *GetSpekeContext(void)
{
    return g_spekeSession;
}

static void SetSpekeSession(const BindSession *session)
{
    g_spekeSession = (BindSession *)session;
}

/* SPEKE协商结束通知函数 */
static int SpekeFinishedProc(int errorCode, const BindSession *spekeSession)
{
    (void)spekeSession;

    OlaLogInfo("speke errcode:%d", errorCode);
    g_spekeErrCode = errorCode;
    if (errorCode == 0) {
        SpekeSetNegoStatus(OLA_NEGO_STATUS_FINISH);
    }

    return OLA_OK;
}

static int InitSpekeSession(void)
{
    g_spekeErrCode = 0;

    /* 初始化speke会话 */
    BindSession *spekeSession = SpekeInitSession(SPEKE_SERVER, (const void *)SpekeFinishedProc, false);
    if (spekeSession == NULL) {
        return OLA_ERROR;
    }

    /* 存储会话句柄 */
    SetSpekeSession(spekeSession);
    OlaLogInfo("init speke session success");

    return OLA_OK;
}

static int InitE2ESpeke(void)
{
    if (GetSpekeContext() == NULL) {
        /* 初始化speke会话 */
        if (InitSpekeSession() != OLA_OK) {
            OlaLogError("init speke session");
            return OLA_ERROR;
        }
    }

    BindSession *spekeSession = GetSpekeContext();
    if (spekeSession == NULL) {
        OlaLogError("speke session is null");
        return OLA_ERROR;
    }
    return OLA_OK;
}

static void FreeSpekeSession(void)
{
    BindSession *session = GetSpekeContext();
    if (session == NULL) {
        return;
    }

    SpekeFreeSession(session);
    SetSpekeSession(NULL);
}

/* 重置speke协商状态 */
static void ResetSpekeResources(void)
{
    (void)memset_s(&g_spekeInfo, sizeof(OlaSpekeInfo), 0, sizeof(OlaSpekeInfo));
    FreeSpekeSession();
    g_spekeErrCode = 0;
}

static int SpekeInit(const OlaNegotiationCb *negoCb)
{
    /* 处理入参回调函数 */
    if ((negoCb == NULL) || (negoCb->sendData == NULL) || (negoCb->getPinCodeCb == NULL)) {
        OlaLogError("invalid cb param");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }

    /* 初始化只需要调一次 */
    if (SpekeGetNegoStatus() != OLA_NEGO_STATUS_UNINIT) {
        OlaLogError("now speke nego status[%d], can not init", SpekeGetNegoStatus());
        return OLA_ERROR;
    }

    g_negoCb.sendData = negoCb->sendData;
    g_negoCb.getPinCodeCb = negoCb->getPinCodeCb;

    SpekeSetNegoStatus(OLA_NEGO_STATUS_INIT);

    return OLA_OK;
}

static int GetSessionIdFromData(const OlaData *inData, OlaData *sessionId)
{
    if ((inData == NULL) || (inData->len == 0) || (inData->data == NULL)) {
        OlaLogError("invalid speke data");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }
    if ((sessionId == NULL)) {
        OlaLogError("out buffer null");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }
    cJSON *spekeJson = cJSON_Parse((char *)inData->data);
    if (spekeJson == NULL) {
        OlaLogError("parse msg failed");
        return OLA_CJSON_ERR_PARSE;
    }
    sessionId->data = (unsigned char *)OlaCopyJsonString(spekeJson, OLA_STRING_SESSIONID);
    cJSON_Delete(spekeJson);
    if (sessionId->data == NULL) {
        OlaLogError("get sessionId failed");
        return OLA_ERROR;
    }
    sessionId->len = strlen((char *)sessionId->data);
    return OLA_OK;
}

static int UpdateSpekeInfo(const OlaData *sessionId, int step)
{
    if ((sessionId == NULL) || (sessionId->data == NULL) || (sessionId->len == 0)) {
        OlaLogError("invalid param");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }

    /* 非步骤一或者步骤二，则认为收到数据无需处理，无需更新状态 */
    if ((step != OLA_SPEKE_STEP_FIRST) && (step != OLA_SPEKE_STEP_FINISH)) {
        OlaLogError("step [%d], do nothing", step);
        return OLA_OK;
    }

    /* 收到初次包数据，需要重置会话，并更新sessionId */
    if (step == OLA_SPEKE_STEP_FIRST) {
        ResetSpekeResources();
        if (strncpy_s(g_spekeInfo.sessionId, sizeof(g_spekeInfo.sessionId),
            (const char *)sessionId->data, sessionId->len) != OLA_OK) {
            OlaLogError("copy sessionId failed");
            return OLA_SECUREC_ERR_STRNCPY;
        }
    }

    /* 收到初次包、第二包数据都需要更新时间 */
    int ret = OlaGetTime(&(g_spekeInfo.lastMsgTime));
    if (ret != OLA_OK) {
        OlaLogError("update time faied, ret[%d]", ret);
        return ret;
    }

    return ret;
}

static int GetSpekeMsgStep(const OlaData *sessionId, int *msgStep)
{
    if ((sessionId == NULL) || (sessionId->data == NULL) || (msgStep == NULL)) {
        OlaLogError("session id invalid");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }

    bool isTimeout = true;
    if (g_spekeInfo.lastMsgTime == 0) {
        isTimeout = true;
    } else if (OlaIsOvertime(&isTimeout, g_spekeInfo.lastMsgTime, SPEKE_NEGO_TIMEOUT) != OLA_OK) {
        OlaLogError("speke,get timeout state fail");
        return OLA_ERROR;
    }

    if ((strlen(g_spekeInfo.sessionId) == 0) || isTimeout) {
        /* 超时或者sessionId为空，当做初次包数据处理 */
        *msgStep = OLA_SPEKE_STEP_FIRST;
    } else if (strcmp(g_spekeInfo.sessionId, (char *)sessionId->data) == OLA_OK) {
        /* 不超时且sessionId相同，则认为是第二包数据，其他情况不处理数据 */
        *msgStep = OLA_SPEKE_STEP_FINISH;
    }

    OlaLogInfo("timeout flag: %d, step: %d", isTimeout, *msgStep);

    if (UpdateSpekeInfo(sessionId, *msgStep) != OLA_OK) {
        OlaLogError("update speke info failed");
        return OLA_ERROR;
    }

    return OLA_OK;
}

static int HandleSpekeMsg(const OlaData *inData, OlaData *outData)
{
    if ((inData == NULL) || (inData->data == NULL) || (outData == NULL) || (outData->data != NULL)) {
        OlaLogError("param null");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }
    if ((inData->len == 0) || (outData->len != 0)) {
        OlaLogError("invalid len, bufLen[%u], outDataLen[%u]", inData->len, outData->len);
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }

    if (InitE2ESpeke() != OLA_OK) {
        OlaLogInfo("init speke failed");
        return OLA_ERROR;
    }

    /* 申请回复报文空间, 在 Transmit 中使用 */
    OlaBuffer msgBuf = {NULL, 0, SPEKE_MAX_BUFF_SIZE};
    msgBuf.buf = (unsigned char *)OlaMalloc(msgBuf.size);
    if (msgBuf.buf == NULL) {
        OlaLogError("malloc failed, len[%u]", msgBuf.size);
        return OLA_OS_ERR_MALLOC;
    }
    (void)memset_s(msgBuf.buf, msgBuf.size, 0, msgBuf.size);

    int ret = SpekeProcessPacket(GetSpekeContext(), (const char *)inData->data, &msgBuf);
    if (ret != OLA_OK) {
        OlaLogError("process speke data failed, ret[%d]", ret);
        OlaPutNullAfterFree((void **)&msgBuf.buf);
        msgBuf.size = 0;
        return ret;
    }

    /* 如果speke过程中出错，则返回错误码 */
    if (g_spekeErrCode != 0) {
        ret = g_spekeErrCode;
        g_spekeErrCode = 0;
    }

    outData->data = msgBuf.buf;
    outData->len = msgBuf.len;

    return OLA_OK;
}

static int HandleFirstMsg(const OlaData *inData, OlaData *outData)
{
    /* 内部函数且入参已在调用函数时判断，因此不重复判断入参 */

    SpekeSetNegoStatus(OLA_NEGO_STATUS_INIT);

    /* 处理初次包数据 */
    int ret = HandleSpekeMsg(inData, outData);
    if (ret != OLA_OK) {
        OlaLogError("handle msg failed, ret[%d]", ret);
        return OLA_ERROR;
    }

    SpekeSetNegoStatus(OLA_NEGO_STATUS_DOING);

    return OLA_OK;
}

static int HandleSecondMsg(const OlaData *inData, OlaData *outData)
{
    /* 内部函数且入参已在调用函数时判断，因此不重复判断入参 */
    if (SpekeGetNegoStatus() != OLA_NEGO_STATUS_DOING) {
        OlaLogError("now nego status[%d], handle secone msg failed", SpekeGetNegoStatus());
        return OLA_ERROR;
    }

    int ret = HandleSpekeMsg(inData, outData);
    if (ret != OLA_OK) {
        OlaLogError("handle msg failed, ret[%d]", ret);
        return OLA_ERROR;
    }

    return OLA_OK;
}

/* SPEKE协商数据入口 */
static int SpekeProcessData(const OlaData *inData, OlaData *outData)
{
    if ((inData == NULL) || (inData->data == NULL) || (inData->len == 0) ||
        (outData == NULL) || (outData->data != NULL) || (outData->len != 0)) {
        OlaLogError("invalid param");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }

    OlaData sessionId = {NULL, 0};
    if ((GetSessionIdFromData(inData, &sessionId) != OLA_OK) || (sessionId.len != SPEKE_SESSION_ID_LEN)) {
        OlaLogError("parse speke buf faild, session id len[%u]", sessionId.len);
        OlaDataFree(&sessionId);
        return OLA_ERROR;
    }

    /* 判断接收到的协商信息是第几包 */
    int msgStep = OLA_SPEKE_STEP_INVALID;
    if (GetSpekeMsgStep(&sessionId, &msgStep) != OLA_OK) {
        OlaLogError("get msg step failed");
        return OLA_ERROR;
    }
    OlaDataFree(&sessionId);

    int ret;
    switch (msgStep) {
        case OLA_SPEKE_STEP_FIRST:
            ret = HandleFirstMsg(inData, outData);
            break;
        case OLA_SPEKE_STEP_FINISH:
            ret = HandleSecondMsg(inData, outData);
            break;
        default:
            OlaLogError("defalut msg step");
            ret = OLA_ERROR;
            break;
    }

    OlaLogInfo("speke data process, msgStep[%d], ret[%d]", msgStep, ret);
    return ret;
}

/* 检查加解密入参数据是否正确 */
static int CheckDataParamAndStatus(const OlaData *inData, const OlaData *outData, bool isDecode)
{
    if ((inData == NULL) || (inData->data == NULL) || (inData->len == 0)) {
        OlaLogError("in data invalid");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }

    if (isDecode && (inData->len < (OLA_TYPICAL_GCM_IV_LEN + OLA_TYPICAL_GCM_VERSION_LEN + OLA_TYPICAL_GCM_TAG_LEN))) {
        OlaLogError("in data len invalid, inLen[%u]", inData->len);
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }

    if ((outData == NULL) || (outData->data != NULL) || (outData->len != 0)) {
        OlaLogError("out data invalid");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }

    if (SpekeGetNegoStatus() != OLA_NEGO_STATUS_FINISH) {
        OlaLogError("negotiation is unfinished");
        return OLA_ERROR;
    }

    return OLA_OK;
}

static int SpekeEncodeData(const OlaData *inData, OlaData *outData)
{
    if (CheckDataParamAndStatus(inData, outData, false) != OLA_OK) {
        return OLA_ERROR;
    }
    OlaData base64 = {NULL, 0};
    /* TODO speke加密报文待定，当前|ver(1byte)|iv(12bytes)|data|tag(16bytes)|且为aes128gcm加密 */
    base64.len = inData->len + OLA_TYPICAL_GCM_VERSION_LEN + OLA_TYPICAL_GCM_IV_LEN + OLA_TYPICAL_GCM_TAG_LEN;
    base64.data = OlaMalloc(base64.len);
    if (base64.data == NULL) {
        OlaLogError("malloc error");
        return OLA_OS_ERR_MALLOC;
    }
    base64.data[0] = OLA_TYPICAL_GCM_VERSION;
    int ret;
    do {
        ret = OlaRand(base64.data + OLA_TYPICAL_GCM_VERSION_LEN, OLA_TYPICAL_GCM_IV_LEN);
        if (ret != OLA_OK) {
            OlaLogError("gen iv failed");
            break;
        }

        const BindSession *session = (const BindSession *)GetSpekeContext();
        if (session == NULL) {
            ret = OLA_ERROR;
            OlaLogError("speke context null");
            break;
        }

        AesGcmParam inputParam = {
            .key = {(unsigned char *)(session->dataEncKey), OLA_AES_128_GCM_KEY_LEN},
            .iv = {base64.data + OLA_TYPICAL_GCM_VERSION_LEN, OLA_TYPICAL_GCM_IV_LEN},
            .add = {NULL, 0},
            .tag = {NULL, 0},
            .inData = {inData->data, inData->len},
        };

        OlaBuffer encData = {base64.data + OLA_TYPICAL_GCM_VERSION_LEN + OLA_TYPICAL_GCM_IV_LEN, 0, inData->len};
        OlaBuffer tag = {base64.data + OLA_TYPICAL_GCM_VERSION_LEN + OLA_TYPICAL_GCM_IV_LEN + inData->len,
            0, OLA_TYPICAL_GCM_TAG_LEN};
        ret = OlaAesGcmEncript(&inputParam, &encData, &tag);
        if (ret != OLA_OK) {
            OlaLogWarning("aes-128-gcm encript error");
            break;
        }
        ret = OlaEncodeBase64Data(&base64, outData);
        if (ret != OLA_OK) {
            OlaLogWarning("base 64 encode error");
            break;
        }
        OlaDataFree(&base64);
        return OLA_OK;
    } while (0);

    OlaDataFree(outData);
    OlaDataFree(&base64);
    return ret;
}

static int SpekeDecodeData(const OlaData *inData, OlaData *outData)
{
    if (CheckDataParamAndStatus(inData, outData, true) != OLA_OK) {
        return OLA_ERROR;
    }
    OlaData base64Data = {NULL, 0};
    int ret = OlaDecodeBase64Data(inData, &base64Data);
    if (ret != OLA_OK) {
        OlaLogError("base64 decode data failed, ret[%d]", ret);
        return ret;
    }

    const BindSession *session = (const BindSession *)GetSpekeContext();
    if (session == NULL) {
        OlaLogError("speke context null");
        return OLA_ERROR;
    }

    /* TODO 解密组包形式未定，当前|ver(1byte)|iv(12bytes)|data|tag(16bytes)|且为aes128gcm解密 */
    AesGcmParam inputParam = {
        .key = {(unsigned char *)(session->dataEncKey), OLA_AES_128_GCM_KEY_LEN},
        .iv = {base64Data.data + OLA_TYPICAL_GCM_VERSION_LEN, OLA_TYPICAL_GCM_IV_LEN},
        .add = {NULL, 0},
        .tag = {base64Data.data + base64Data.len - OLA_TYPICAL_GCM_TAG_LEN, OLA_TYPICAL_GCM_TAG_LEN},
        .inData = {
            base64Data.data + OLA_TYPICAL_GCM_VERSION_LEN + OLA_TYPICAL_GCM_IV_LEN,
            base64Data.len - OLA_TYPICAL_GCM_VERSION_LEN - OLA_TYPICAL_GCM_IV_LEN - OLA_TYPICAL_GCM_TAG_LEN
        },
    };

    outData->data = OlaMalloc(inputParam.inData.len + 1);
    if (outData->data == NULL) {
        OlaPutNullAfterFree((void**)&base64Data.data);
        OlaLogError("malloc error");
        return OLA_OS_ERR_MALLOC;
    }
    outData->data[inputParam.inData.len] = 0;
    OlaBuffer outBuffer = {outData->data, 0, inputParam.inData.len + 1};
    ret = OlaAesGcmDecript(&inputParam, &outBuffer);
    if (ret != OLA_OK) {
        OlaDataFree(outData);
        outBuffer.buf = NULL;
    } else {
        outData->len = outBuffer.len;
    }
    OlaDataFree(&base64Data);
    return ret;
}

static void SpekeDeinit(void)
{
    /* 重置所有内容，如sessionKey、状态、session回调函数 */
    SpekeSetNegoStatus(OLA_NEGO_STATUS_UNINIT);

    ResetSpekeResources();

    g_negoCb.sendData = NULL;
    g_negoCb.getPinCodeCb = NULL;
}

static OlaNegotiationHook g_spekeNegoHook = {
    .init = SpekeInit,
    .processData = SpekeProcessData,
    .getStatus = SpekeGetNegoStatus,
    .encodeData = SpekeEncodeData,
    .decodeData = SpekeDecodeData,
    .deinit = SpekeDeinit,
};

const OlaNegotiationHook *OlaGetSpekeNegoHooks(void)
{
    return &g_spekeNegoHook;
}
